﻿using System;
using System.Windows.Input;

namespace ItemsViewerDemo
{
    /// <summary>
    /// Implements the <see cref="ICommand"/> interface for the View layer architecture.
    /// </summary>
    /// <remarks>
    /// <para>
    /// This class is a lightweight version of the <see cref="RoutedCommand"/> class as
    /// it doesn't implement the routed part of the class - as a result, the definition
    /// of <see cref="CommandBinding"/> doesn't work with instances of this class.
    /// </para>
    /// </remarks>
    public class Command : ICommand
    {
        /// <summary>
        /// The delegate used to execute the command.
        /// </summary>
        private readonly Action<ICommand, object> execute;

        /// <summary>
        /// The delegate used to check if the command can be executed.
        /// </summary>
        private readonly Func<ICommand, object, bool> canExecute;

        /// <summary>
        /// Initializes a new instance of the Command class
        /// that does nothing.
        /// </summary>
        public Command()
            : this(null, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the Command class with
        /// a specific code that is always available.
        /// </summary>
        /// <param name="execute">
        /// The delegate used to execute the command.
        /// </param>
        public Command(Action<ICommand, object> execute)
            : this(execute, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the Command class with
        /// a specific code and the associated delegates.
        /// </summary>
        /// <param name="execute">
        /// The delegate used to execute the command.
        /// </param>
        /// <param name="canExecute">
        /// The delegate used to check if the command can be executed.
        /// </param>
        /// <remarks>
        /// The delegate associated with <paramref name="canExecute"/> will be called very often.
        /// The implemention should be based on cached result.
        /// </remarks>
        public Command(Action<ICommand, object> execute, Func<ICommand, object, bool> canExecute)
        {
            this.execute = execute;
            this.canExecute = canExecute;
        }

        /// <summary>
        /// Occurs when the execution status change.
        /// </summary>
        /// <remarks>
        /// This event is linked to the <see cref="CommandManager.RequerySuggested"/> event.
        /// </remarks>
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        // TBD: public RaiseExecuteChanged() - useful with SL, here we have CommandManager.RequerySuggested - perf ?

        /// <summary>
        /// Checks if the class is in a valid state to be executed.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method calls the <c>canExecute</c> delegate defined during the initialization
        /// of the object.
        /// </para>
        /// <para>
        /// This method returns always <c>true</c>, if the command is not linked to
        /// a <c>canExecute</c> delegate.
        /// </para>
        /// </remarks>
        /// <param name="parameter">
        /// The parameter of the command.
        /// </param>
        /// <returns>
        /// <c>true</c> if the command can be executed, <c>false</c> otherwise.
        /// </returns>
        public bool CanExecute(object parameter)
        {
            // TBD: Good bool optimisation !
            return this.canExecute == null
                || this.canExecute(this, parameter);
        }

        /// <summary>
        /// Executes the command.
        /// </summary>
        /// <remarks>
        /// This method calls the <c>execute</c> delegate defined during the initialization
        /// of the object.
        /// </remarks>
        /// <param name="parameter">
        /// The parameter of the command.
        /// </param>
        public void Execute(object parameter)
        {
            if (this.execute != null && this.CanExecute(parameter))
            {
                this.execute(this, parameter);
            }
        }
    }
}
